package cn.wx.common.unitofwork.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * 切点类
 */
@SuppressWarnings({"rawtypes","unchecked"})
@Aspect
@Component
@Order(1)
public class UnitOfWorkAspect {

	@Value("${unit.project}")
	private String project;
	
	@Value("${unit.environment}")
	private String environment;
	
	// Service层切点
	@Pointcut("@annotation(cn.wx.common.unitofwork.aop.UseUnitOfWork)")
	public void serviceAspect() {
	}

	/**
	 * 前置函数给 任务附上UUID
	 * @param joinPoint
	 */
	@Before("serviceAspect()")
	public void doBefore(JoinPoint joinPoint) {
		
	}

	@After("serviceAspect()")
	public void doAfter(JoinPoint joinPoint) {
		Object[] arguments = joinPoint.getArgs();
		if (arguments != null && arguments.length > 0) {
			UseUnitOfWork uw = null;
			try {
				Object payload = arguments[0];
				uw = getMthodUseUnitOfWork(joinPoint);
				UnitOfWork unitOfWork = UnitOfWork.getInstance(uw.saga());
				unitOfWork.generateEvent(payload, project, environment);
				Long sagaId = unitOfWork.publish();
				setMthodSagaId(joinPoint,sagaId);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 用于拦截service层记录业务事件日志
	 * 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		Object[] arguments = joinPoint.getArgs();
		if (arguments != null && arguments.length > 0) {
			UseUnitOfWork uw = null;
			try {
				uw = getMthodUseUnitOfWork(joinPoint);
				UnitOfWork unitOfWork = UnitOfWork.getInstance(uw.saga());
				unitOfWork.publishThrowing(uw.sagaId());
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	/**
	 * 获取注解中对方法的描述信息 用于service层注解
	 * 
	 * @param joinPoint
	 *            切点
	 * @return 方法描述
	 * @throws Exception
	 */
	public static void setMthodSagaId(JoinPoint joinPoint,Long sagaId) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class<?> targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					UseUnitOfWork foo = method.getAnnotation(UseUnitOfWork.class);
			        InvocationHandler h = Proxy.getInvocationHandler(foo);
			        Field hField = h.getClass().getDeclaredField("memberValues");
			        hField.setAccessible(true);
					Map memberValues = (Map) hField.get(h);
			        memberValues.put("sagaId", sagaId);
					break;
				}
			}
		}
	}

	/**
	 * 获取方法上的注解
	 * @return
	 */
	public static UseUnitOfWork getMthodUseUnitOfWork(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class<?> targetClass = Class.forName(targetName);
		UseUnitOfWork useUnitOfWork = null;
		Method[] methods = targetClass.getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					useUnitOfWork = method.getAnnotation(UseUnitOfWork.class);
					break;
				}
			}
		}
		return useUnitOfWork;
	}
	
	
	public String getProject() {
		return project;
	}

	public void setProject(String project) {
		this.project = project;
	}

	public String getEnvironment() {
		return environment;
	}

	public void setEnvironment(String environment) {
		this.environment = environment;
	}
	
}